Now, what actually happened behind the scenes? Well, this is what Node does. It
takes your JavaScript code, it compiles it into machine code, and executes it. In
the preceding code, you can see it executed our code, printing out Hello world!.
Now, the V8 engine is running behind the scenes when we execute this
command, and it's also running inside the Chrome browser.
If I open up the developer tools in Chrome by going to Settings | More Tools |
Developer Tools:
I can ignore most of the things. I'm just looking for the Console tab, as shown in
the following screenshot:
The preceding screenshot showing the console is a place where we can run some
JavaScript code. I can type the exact same command,
console.log('Hello world!');
and
run it:
As you can see in the preceding screenshot, Hello world! prints to the screen,
which is the exact same result we got when we ran it up earlier using Terminal.
In both cases, we're running it through the V8 engine, and in both cases the
output is the same.
Now, we already know that the two are different. Node has features such as
filesystem manipulation, and the browser has features such as manipulating
what's shown inside the window. Let's take a quick moment to explore their
differences.
Differences between JavaScript
coding using Node and in the
browser
Inside the browser, you've probably used
window
if you've done any JavaScript
development:
Window is the global object, it stores basically everything you have access to. In
the following screenshot, you can see things such as array, we have all sorts of
CSS manipulation and Google Analytics keywords; essentially every variable
you create lives inside Window:
We have something similar inside Node called
global
, as shown here:
It's not called
window
because there is no browser window in Node, thus it is called
global
. The
global
object stores a lot of the same things as
window
. In the following
screenshot, you can see methods that might be familiar, such as
setTimeout
and
setInterval:
If we look at this code screenshot, we have most of the things that are defined
inside the window, with some exceptions, as shown in the following screenshot:
Now, inside the Chrome browser, I also have access to
document
:
The
document
object stores a reference to the
Document Object Model
(
DOM
) in
the Node website. The
document
object shows exactly what I have inside the
browser's viewport, as shown in the following screenshot:
I can make changes to the document to update what gets shown up on the
browser's viewport. Now, obviously we don't have this HTML
document
inside
Node, but we do have something similar, which is called
process
. You can view it
by running process from Node, and in the following screenshot, we have a lot of
information about the specific Node process that's being executed:
There's also methods available here to shut down the current Node process.
What I'd like you to do is run the
process.exit
command, passing in as an
argument the number zero, to say that things exited without error:
process.exit(0);
When I run this command, you can see I'm now back at the command prompt, as
shown in the following screenshot:
I've left Node, and I'm at a place where I can run any regular command prompt
command, such as checking my Node version. I can always get back into Node
by running
node
, and I can leave it without using the
process.exit
command by
using control + C twice.
Now, I'm back at my regular command prompt. So, these are the notable
differences, obviously inside the browser you have the viewable area, window
gets changed to global, and a document basically becomes process. Now,
obviously that's a generalization, but those are some of the big picture changes.
We'll be exploring all the minutiae throughout the book.
Now, when someone asks you what is Node? You can say Node's a JavaScript
runtime that uses the V8 engine. When they ask you what the V8 engine is, you
can say the V8 engine is an open source JavaScript engine written in C++ that
takes JavaScript code and compiles it to machine code. It's used inside Node.js
and it's used in the Chrome browser.
Why use Node
In this section, we'll cover the why behind Node.js. Why is it so good at creating
backend apps? And why is it becoming so popular with companies such as
Netflix, Uber and Walmart, who are all using Node.js in production?
As you might have noticed since you're taking this course, when people want to
learn a new backend language, more and more they're turning to Node as the
language they want to learn. The Node skillset is in hot demand, for both
frontend developers who need to use Node day to day to do things such as
compile their applications, to engineers who are creating applications and
utilities using Node.js. All of this has made Node the backend language of
choice.
Now, if we look at the homepage of Node, we have three sentences, as shown in
the following screenshot:
In the previous section, we addressed the first sentence. We took a look at what
Node.js is. There's only three sentences in the image, so in this section, we'll take
a look at the second two sentences. We'll read them now, then we'll break it
down, learning exactly why Node is so great.
The first sentence, Node.js uses an event-driven, non-blocking I/O model that
makes it lightweight and efficient; we'll explore all of this now. The second
sentence we'll explore at the end of the section—Node.js' packaged ecosystem,
npm, is the largest ecosystem of open source libraries in the world. Now, these
two sentences have a ton of information packed into them.
We'll go over a few code examples, we'll dive into some charts and graphs, and
we'll explore what makes Node different and what makes it so great.
Node is an event-driven, non-blocking language. Now, what is I/O? I/O is
something that your application does all of the time. When you're reading or
writing to a database, that is I/O, which is short form for input/output.
This is the communication from your Node application to other things inside of
the Internet of Things. This could be a database read and write request, you may
be changing some files on your filesystem, or you may be making an HTTP
request to a separate web server, such as a Google API for fetching a map for the
user's current location. All of these use I/O, and I/O takes time.
Now, the non-blocking I/O is great. That means while one user is requesting a
URL from Google, other users can be requesting a database file read and write
access, they can be requesting all sorts of things without preventing anyone else
from getting some work done.
Blocking and non-blocking
software development
Let's go ahead and take a look at the differences between blocking and non
blocking software development:
In the preceding screenshot, I have two files that we'll be executing. But before
going to that, first let's explore how each of these files operates, the steps that are
required in order to finish the program.
This will help us understand the big differences between blocking, which I have
on the left side of the image, which is not what Node uses, and non-blocking is
on the right side, which is exactly how all of our Node applications in the book
are going to operate.
You don't have to understand the individual details, such as what require is, in
order to understand what's going on in this code example. We'll be breaking
things down in a very general sense. The first line on each code is responsible
for fetching a function that gets called. This function will be our simulated I/O
function that is going to a database, fetching some user data and printing it to the
screen.
Refer to the preceding code image. After we load in the function, both files try to
fetch a user with an ID of
123
. When it gets that user, it prints it to the screen with
the
user1
string first, and then it goes on and it fetches the user with
321
as the ID.
And it prints that to the screen. And finally both files add up
1 + 2
, storing the
result, which is 3, in the
sum
variable and print it to the screen.
Now, while they all do the same thing, they do it in very different ways. Let's
break down the individual steps. In the following code image, we'll go over what
Node executes and how long it takes:
You can consider the seconds shown in the preceding screenshot; it doesn't really
matter, it's just to show the relative operating speed between the two files.
The working of blocking I/O
The blocking example can be illustrated as follows:
The first thing that happens inside our blocking example, as shown in the
preceding screenshot, is that we fetch the user on line 3 in the code:
var user1 = getUserSync('123');
Now, this request requires us to go to a database, which is an I/O operation to
fetch that user by ID. This takes a little bit of time. In our case, we'll say it takes
three seconds.
Next, on line 4 in the code, we print the user to the screen, which is not an I/O
operation and it runs right away, printing
user1
to the screen, as shown in the
following code:
console.log('user1', user1);
As you can see in the following screenshot, it takes almost no time at all:
Next up, we wait on the fetching of
user2
:
var user2 = getUserSync('321');
When
user2
comes back, as you might expect, we print it to the screen, which is
exactly what happens on line 7:
console.log('user2', user2);
Finally, we add up our numbers and we print it to the screen:
var sum = 1 + 2; console.log('The sum
is ' + sum);
None of this is I/O, so right here we have our sum printing to the screen in barely
any time.
This is how blocking works. It's called blocking because while we're fetching
from the database, which is an I/O operation, our application cannot do anything
else. This means our machine sits around idle waiting for the database to
respond, and can't even do something simple like adding two numbers and
printing them to the screen. It's just not possible in a blocking system.
The working non-blocking I/O
In our non-blocking example, this is how we'll be building our Node
applications.
Let's break this code example down line by line. First up, things start much the
same way as we discussed in the blocking example. We'll start the
getUser
function for
user1
, which is exactly what we did earlier:
But we're not waiting, we're simply kicking off that event. This is all part of the
event loop inside Node.js, which is something we'll be exploring in detail.
Notice it takes a little bit of time; we're just starting the request, we're not
waiting for that data. The next thing we do might surprise you. We're not
printing
user1
to the screen because we're still waiting for that request to come
back, instead we start the process of fetching our
user2
with the ID of
321
:
In this part of the code, we're kicking off another event, which takes just a little
bit of time to do-it is not an I/O operation. Now, behind the scenes, the fetching
of the database is I/O, but starting the event, calling this function is not, so it
happens really quickly.
Next up, we print the sum. The sum doesn't care about either of the two user
objects. They're basically unrelated, so there's no need to wait for the users to
come back before I print that
sum
variable, as shown in the following screenshot:
What happens after we print the sum? Well, we have the dotted box, as shown in
the following screenshot:
This box signifies the simulated time it takes for our event to get responded to.
Now, this box is the exact same width as the box in the first part of the blocking
example (waiting on user1), as shown here:
Using non-blocking doesn't make our I/O operations any faster, but what it does
do is it lets us run more than one operation at the same time.
In the non-blocking example, we start two I/O operations before the half second
mark, and in between three and a half seconds, both come back, as shown in the
following screenshot:
Now, the result here is that the entire application finishes much quicker. If you
compare the time taken in executing both the files, the non-blocking version
finishes in just over three seconds, while the blocking version takes just over six
seconds. A difference of 50%. This 50% comes from the fact that in blocking,
we have two requests each taking three seconds, and in non-blocking, we have
two requests each taking three seconds, but they run at the same time.
Using the non-blocking model, we can still do stuff like printing the sum without
having to wait for our database to respond. Now, this is the big difference
between the two; blocking, everything happens in order, and in non-blocking we
start events, attaching callbacks, and these callbacks get fired later. We're still
printing out
user1
and
user2
, we're just doing it when the data comes back,
because the data doesn't come back right away.
Inside Node.js, the event loop attaches a listener for the event to finish, in this
case for that database to respond back. When it does, it calls the callback you
pass in the non-blocking case, and then we print it to the screen.
Now, imagine this was a web server instead of the preceding
example. That would mean if a web server comes in looking to
query the database, we can't process other users' requests without
spinning up a separate thread. Now, Node.js is single threaded,
which means your application runs on one single thread, but since
we have non-blocking I/O, that's not a problem.
In a blocking context, we could handle two requests on two separate threads, but
that doesn't really scale well, because for each request we have to beef up the
amount of CPU and RAM resources that we're using for the application, and this
sucks because those threads, are still sitting idle. Just because we can spin up
other threads doesn't mean we should, we're wasting resources that are doing
nothing.
In the non-blocking case, instead of wasting resources by creating multiple
threads, we're doing everything on one thread. When a request comes in, the I/O
is non-blocking so we're not taking up any more resources than we would be if it
never happened at all.
Blocking and non-blocking
examples using Terminal
Let's run these examples in real time and see what we get. And we have the two
files (
blocking
and
non-blocking
files) that we saw in the previous section.
We'll run both of these files, and I'm using the Atom editor to edit my text files.
These are things we'll be setting up later in the section, this is just for your
viewing purpose, you don't need to run these files.
Now, the
blocking
and
non-blocking
files, will both get run and they'll do similar
things to those we did in the previous section, just in a different way. Both use
I/O operations,
getUserSync
and
getUser
, that take five seconds apiece. The time is
no different, it's just the order they execute in that makes the non-blocking
version much quicker.
Now, to simulate and show how things work, I'll add a few
console.log
statements
as shown in the following code example,
console.log('starting user1')
,
console.log('starting user2')
.
This will let us visualize how things work inside Terminal. By running
node
blocking.js
, this is how we run files. We type
node
and we specify the filename, as
shown in the following code:
node blocking.js
When I run the file, we get some output. starting user1 prints to the screen and
then it sits there:
Now, we have the user1 object printing to the screen with the name Andrew, and
starting user2 prints to the screen, as shown in the following code output:
After that, the user2 object comes back around five seconds later with the name
of Jen.
As shown in the preceding screenshot, our two users have printed to the screen,
and at the very end our sum, which is 3, prints to the screen; everything works
great.
Notice that starting user1 was immediately followed by the finishing of user1,
and starting user2 was immediately followed by the finishing of user2 because
this is a blocking application.
Now, we'll run the non-blocking file, which I've called
non-blocking.js
. When I run
this file, starting user1 prints, starting user2 prints, then the sum prints all back to
back:
Around 5 seconds later, at basically the same time, user1 and user2 both print to
the screen.
This is how non-blocking works. Just because we started an I/O operation
doesn't mean we can't do other things, such as starting another one and printing
some data to the screen, in this case just a number. This is the big difference, and
this is what makes non-blocking apps so fantastic. They can do so many things
at the exact same time without having to worry about the confusion of multi-
threading applications.
Let's move back into the browser and take a look at those sentences again in the
Node website:
Node.js uses an event-driven, non-blocking I/O model that makes it lightweight
and efficient, and we saw that in action.
Because Node is non-blocking, we were able to cut down the time our
application took by half. This non-blocking I/O makes our apps super quick, this
is where the lightweight and efficient comes into play.
Node community – problem
solving open source libraries
Now, let's go to the last sentence on the Node website, as shown in the following
screenshot:
Node.js' package ecosystem, npm, is the largest ecosystem of open-source
libraries in the world. This is what really makes Node fantastic. This is the
cherry on top-the community, the people every day developing new libraries that
solve common problems in your Node.js applications.
Things such as validating objects, creating servers, and serving up content live
using sockets. There's libraries already built for all of those so you don't have to
worry about this. This means that you can focus on the specific things related to
your application without having to create all this infrastructure before you can
even write real code, code that does something specific to your apps use case.
Now, npm, which is available on
npmjs.org
, is the site we'll be turning to for a lot
of third-party modules:
If you're trying to solve a problem in Node that sounds generic, chances are that
someone's already solved it. For example, if I want to validate some objects, let's
say I want to validate that a name property exists and that there's an ID with a
length of three. I could go into Google or go into npm; I usually choose Google,
and I could Google search
npm validate object
.
When I google that, I'll just look for results from
npmjs.com
, and you can find the
first three or so are from that:
I can click the first one, and this will let me explore the documentation and see if
it's right for me:
This one looks great, so I can add it to my app without any effort.
Now, we'll go through this process. Don't worry, I'm not going to leave you high
and dry on how to add third-party modules. We'll be using a ton of them in the
book because this is what real Node developers do. They take advantage of the
fantastic community of developers, and that's the last thing that makes Node so
great.
This is why Node has come to the position of power that it currently sits at,
because it's non-blocking, meaning it's great for I/O applications, and it has a
fantastic community of developers. So, if you ever want to get anything done,
there's a chance someone already wrote the code to do it.
This is not to say you should never use Rails or Python or any other blocking
language again, that is not what I'm getting at. What I'm really trying to show
you is the power of Node.js and how you can make your applications even
better. Languages like Python have things such as the library Twisted, which
aims to add non-blocking features to Python. Though the big problem is all of
the third-party libraries, as they are still written in a blocking fashion, so you're
really limited as to which libraries you can use.
Since Node was built non-blocking from the ground up, every single library on
n
pmjs.com
is non-blocking. So you don't have to worry about finding one that's non
blocking versus blocking; you can install a module knowing it was built from the
ground up using a non blocking ideology.
In the next couple of sections, you'll be writing your very first app and running it
from Terminal.
Different text editors for node
applications
In this section, I want to give you a tour of the various text editors you can use
for this book. If you already have one you love using, you can keep using the
one you have. There's no need to switch editors to get anything done in this
book.
Now, if you don't have one and you're looking for a few options, I always
recommend using
Atom
, which you can find at
atom.io
. It's free, open source, and
it's available on all operating systems, Linux, macOS, and Windows. It's created
by the folks behind GitHub and it's the editor that I'll be using inside of this
book. There's an awesome community of theme and plug-in developers so you
really can customize it to your liking.
Now, aside from Atom there are a few other options. I've heard a lot of people
talking about
Visual Studio Code
. It is also open source, free, and available on
all operating systems. If you don't like Atom, I highly recommend you check this
out, because I've heard so many good things by word of mouth.
Next up, we always have
Sublime Text
, which you can find at
sublimetext.com
.
Now, Sublime Text is not free and it's not open source, but it's a text editor that a
lot of folks do enjoy using. I prefer Atom because it's very similar to Sublime
Text, though I find it snappier and easier to use, plus it's free and open source.
Now, if you are looking for a more premium editor with all of the bells and
whistles in IDE as opposed to a text editor, I always recommend
JetBrains
.
None of their products are free, though they do come with a 30-day free trial, but
they really are the best tools of the trade. If you find yourself in a corporate
setting or you're at a job where the company is willing to pay for an editor, I
always recommend that you go with JetBrains. All of their editors come with all
of the tools you'd expect, such as version control integration, debugging tools,
and deploying tools built in.
So, take a moment, download the one you want to use, play around with it, make
sure it fits your needs, and if not, try another one.
Hello World – creating and
running the first Node app
In this section, you will be creating and running your very first Node app. Well,
it will be a simple one. It'll demonstrate the entire process, from creating files to
running them from Terminal.
Creating the Node application
The first step will be to create a folder. Every project we create will go live
inside of its own folder. I'll open up the
Finder
on macOS and navigate to my
desktop. What I'd like you to do is open up the desktop on your OS, whether
you're on Linux, Windows, or macOS, and create a brand new folder called
hello-world.
I don't recommend using spaces in your project file or folder
names, as it only makes it more confusing to navigate inside of
Terminal. Now, we have this
hello-world
folder and we can open it
up inside of our editor.
Now I'll use command + O (Ctrl + O for Windows users) to open up, and I'll
navigate to the desktop and double-click my hello-world folder, as shown here:
On the left I have my files, which are none. So, let's create a new one. I'll make a
new file in the root of the project, and we'll call this one
app.js
, as shown here:
This will be the only file we have inside our Node application, and in this file we
can write some code that will get executed when we start the app.
In the future, we'll be doing crazy stuff like initializing databases and starting
web servers, but for now we'll simply use
console.log
, which means we're
accessing the log property on the console object. It's a function, so we can call it
with parentheses, and we'll pass in one argument as string,
Hello world!
. I'll toss a
semicolon on the end and save the file, as shown in the following code:
console.log('Hello world!');
This will be the first app we run.
Now, remember, there is a basic JavaScript requirement for this
course, so nothing here should look too foreign to you. I'll be
covering everything new and fresh inside of this course, but the
basics, creating variables, calling functions, those should be
something you're already familiar with.
Running the Node application
Now that we have our
app.js
file, the only thing left to do is to run it, and we'll do
that over in Terminal. Now, to run this program, we have to navigate into our
project folder. If you're not familiar with Terminal, I'll give you a quick refresher.
You can always figure out where you are using
pwd
on Linux or macOS, or the
dir
command on Windows. When you run it, you'll see something similar to the
following screenshot:
I'm in the
Users
folder, and then I'm in my user folder, and my user name happens
to be
Gary
.
When you open Terminal or Command Prompt, you'll start in your
user directory.
We can use
cd
to navigate into the desktop, and here we are:
Now we're sitting in the desktop. The other command you can run from
anywhere on your computer is
cd /users/Gary/desktop
. And this will navigate to your
desktop, no matter what folder you're located in. The command
cd desktop
,
requires you to be in the user directory to work correctly.
Now we can start by cd-ing into our project directory, which we called
hello-
world,
as shown in the following command:
cd hello-world
With the following screenshot:
Once we're in this directory, we can run at the
ls
command on Linux or Mac
(which is the
dir
command on Windows) to see all of our files, and in this case
we just have one, we have
app.js
:
This is the file we'll run.
Now, before you do anything else, make sure you are in the
hello-world
folder and
you should have the
app.js
file inside. If you do, all we'll do is run the
node
command followed by a space so we can pass in an argument, and that argument
will be the filename,
app.js
as shown here:
node app.js
Once you have this in place, hit enter and there we go, Hello world! prints to the
screen, as shown here:
And that is all it takes to create and run a very basic Node application. While our
app doesn't do anything cool, we'll be using this process of creating folders/files
and running them from Terminal throughout the book, so it's a great start on our
way to making real-world Node apps.
Summary
In this chapter, we touched base with the concept of Node.js. We took a look at
what Node is and we learned that it's built on top of the V8 JavaScript engine.
Then we explored why Node has become so popular, its advantages and its
disadvantages. We took a look at the different text editors we can choose from
and, at the end, you created your very first Node application.
In the next chapter, we'll dive in and create our first app. I am really excited to
start writing real-world applications.
Node Fundamentals – Part 1
In this chapter, you'll learn a ton about building Node applications, and you'll
actually build your first Node application. This is where all the really fun stuff is
going to start.
We'll kick things off by learning about all of the modules that come built in to
Node. These are objects and functions that let you do stuff with JavaScript
you've never been able to do before. We'll learn how to do things, such as
reading and writing from the filesystem, which we'll use in the Node's
application to persist our data.
We'll also be looking at third-party npm modules; this is a big part of the reason
that Node became so popular. The npm modules give you a great collection of
third-party libraries you can use, and they also have really common problems. So
you don't have to rewrite that boilerplate code over and over again. We'll be
using a third-party module in this chapter to help with fetching input from the
user.
The chapter will specifically cover the following topics:
Module basics
Require own files
Third-party modules
Global modules
Getting input
Module basics
In this section, you will finally learn some Node.js code, and we'll kick things
off by talking about modules inside Node. Modules are units of functionality, so
imagine I create a few functions that do something similar, such as a few
functions that help with math problems, for example, add, subtract, and divide. I
could bundle those up as a module, call it Andrew-math, and other people could
take advantage of it.
Now, we'll not be looking at how to make our own module; in fact, we will be
looking at how we can use modules, and that will be done using a function in
Node, called
require()
. The
require()
function will let us do three things:
First, it'll let us load in modules that come bundled with Node.js. These
include the HTTP module, which lets us make a web server, and the
fs
module, which lets us access the filesystem for our machine.
We will also be using
require()
in later sections to load in third-
party libraries, such as Express and Sequelize, which will let us
write less code.
We'll be able to use prewritten libraries to handle complex problems, and all
we need to do is implement
require()
by calling a few methods.
We will use
require()
to require our very own files. It will let us break up our
application into multiple, smaller files, which is essential for building real-
world apps.
If you have all of your code in one file, it will be really hard to test, maintain,
and update. Now,
require()
isn't that bad. In this section, we'll explore the first use
case for
require()
.
Using case for require()
We'll take a look at two built-in modules; we'll figure out how to require them
and how to use them, and then we'll move on to starting the process of building
that Node application.
Initialization of an application
The first step we'll take inside of the Terminal is that we'll make a directory to
store all of these files. We'll navigate from our home directory to the desktop
using the
cd Desktop
command:
cd Desktop
Then, we'll make a folder to store all of the lesson files for this project.
Now, these lesson files will be available in the resources section for
every section, so if you get stuck or your code just isn't working for
some reason, you can download the lesson files, compare your
files, and figure out where things went wrong.
Now, we'll make that folder using the
mkdir
command, which is the short form for
make directory
. Let's call the folder
notes-node
, as shown in the following code:
mkdir notes-node
We'll make a note app in Node so that
notes-node
seems appropriate. Then we'll
cd
into
notes-node
, and we can get started playing around with some of the built-in
modules:
cd notes-node
These modules are built in, so there's no need to install anything in Terminal. We
can simply require them right inside of our Node files.
The next step in the process is to open up that directory inside the Atom text
editor. So open up the directory we just created on the Desktop, and you will
find it there, as shown in the following screenshot:
Now, we will need to make a file, and we'll put that file in the root of the project:
We'll call this file
app.js
, and this is where our application will start:
We will be writing other files that get used throughout the app, but this is the
only file we'll ever be running from Terminal. This is the initialization file for
our application.
The built-in module to use
require()
Now, to kick things off, the first thing I will do is to use
console.log
to print
Starting app
, as shown in the following code:
console.log('Starting app');
The only reason we'll do this is to keep track of how our files are
executing, and we'll do this only for the first project. Down the line,
once you're comfortable with how files get loaded and how they
run, we'll be able to remove these
console.log
statements, as they
won't be necessary.
After we call the
console.log
starting app, we'll load in a built-in module using
require().
We can get a complete list of all of the built-in modules in the
Node.js API docs.
To view Node.js API docs, go to
nodejs.org/api
. When you go to this URL, you'll
be greeted with a long list of built-in modules. Using the
File System
module
we'll create a new file and the
OS
module. The OS module will let us fetch
things such as the username for the currently logged-in user.
Creating and appending files in
the File System module
To kick things off though, we will start with the File System module. We'll go
through the process of creating a file and appending to it:
When you view a docs page for a built-in module, whether it's File System or a
different module, you'll see a long list of all the different functions and
properties that you have available to you. The one we'll use in this section is
fs.appendFile.
If you click on it, it will take you to the specific documentation, and this is where
we can figure out how to use
appendFile
, as shown in the following screenshot:
Now,
appendFile
is pretty simple. We'll pass to it two string arguments (shown in
the preceding screenshot):
One will be the file name
The other will be the data we want to append to the file
This is all we need to provide in order to call
fs.appendFile
. Before we can call
fs.appendFile
, we need to require it. The whole point of requiring is to let us load
in other modules. In this case, we'll load in the
fs
module from
app.js
.
Let's create a variable that will be a constant, using
const
.
Since we'll not be manipulating the code the module sends back,
there's no need to use the
var
keyword; we will use the
const
keyword.
Then we'll give it a name,
fs
and set it equal to
require()
, as shown in the
following code:
const fs = require()
Here,
require()
is a function that's available to you inside any of your Node.js
files. You don't have to do anything special to call it, you simply call it as shown
in the preceding code. Inside the argument list, we'll just pass one string.
Now, every time you call
require()
, whether you're loading a built-in
module, a third-party module, or your own file, you just pass in one
string.
In our case, we'll pass in the module name, which is
fs
and toss in a semicolon at
the end, as shown in the following code:
const fs = require('fs');
This will tell Node that you want to fetch all of the contents of the
fs
module and
store them in the
fs
variable. At this point, we have access to all of the functions
available on the
fs
module, which we explored over in the docs, including
fs.appendFile.
Back in Atom, we can call the
appendFile
by calling
fs.appendFile
, passing in the two
arguments that we'll use; the first one will be the filename, so we add
greetings.txt
, and the second one will be the text you want to append to the file. In
our case, we'll append
Hello world!
, as shown in the following code:
fs.appendFile('greetings.txt', 'Hello world!');
Let's save the file, as shown in the preceding command, and run it from Terminal
to see what happens.
Warning when running the program on Node v7
If you're running Node v7 or greater, you'll get a little warning
when you run the program inside Terminal. Now, on v7, it'll still
work, it's just a warning, but you can get rid of it using the
following code:
// Orignal line
fs.appendFile('greetings.txt', 'Hello world!');
// Option one
fs.appendFile('greetings.txt', 'Hello world!', function (err){ if (err) {
console.log('Unable to write to file');
}
});
// Option two
fs.appendFileSync('greetings.txt', 'Hello world!');
In the preceding code, we have the original line that we have inside our program.
In
Option one
here is to add a callback as the third argument to the append file.
This callback will get executed when either an error occurs or the file
successfully gets written too. Inside option one, we have an
if
statement; if there
is an error, we simply print a message to the screen,
Unable to write to file
.
Now, our second option in the preceding code,
Option two
, is to call
appendFileSync
,
which is a synchronous method (we'll talk more about that later); this function
does not take the third argument. You can type it as shown in the preceding code
and you won't get the warning.
So, pick one of these two options if you see the warning; both will work much
the same.
If you are on v6, you can stick with the the original line, shown at the top of the
preceding code, although you might as well use one of the two options below
that line to make your code a little more future proof.
Fear not, we'll be talking about asynchronous and synchronous functions, as well
as callback functions, extensively throughout the book. What I'm giving you
here in the code is just a template, something you can write in your file to get
that error removed. In a few chapters, you will understand exactly what these
two methods are and how they work.
If we do the appending over in Terminal,
node app.js
, we'll see something pretty
cool:
As shown in the preceding code, we get our one
console.log
statement,
Starting app.
.
So we know the app started correctly. Also, if we head over into Atom, we'll
actually see that there's a brand new
greetings.txt
file, as shown in the following
code. This is the text file that was created by
fs.appendFile
:
console.log('Starting app.'); const fs
= require('fs');
fs.appendFile('greetings.txt', 'Hello world!');
Here,
fs.appendFile
tries to append
greetings.txt
to a file; if the file doesn't exist, it
simply creates it:
You can see that we have our message,
Hello world!
in the
greetings.txt
file, printing
to the screen. In just a few minutes, we were able to load in a built-in Node
module and call a function that lets us create a brand new file.
If we call it again by rerunning the command using the up arrow key and the
enter key, and we head back into the contents of
greetings.txt
, you can see this
time around that we have
Hello world!
twice, as shown here:
It appended
Hello world!
one time for each time we ran the program. We have an
application that creates a brand new file on our filesystem, and if the file already
exists, it simply adds to it.
The OS module in require()
Once we have created and appended the
greetings.txt
file, we'll customize this
greeting.txt
file. To do this, we'll explore one more built-in module. We'll be
using more than just
appendFile
in the future. We'll be exploring other methods.
For this section, the real goal is to understand
require()
. The
require()
function lets us
load in the module's functionality so that we can call it.
The second module that we'll be using is OS, and we can view it in the
documentation. In the OS module, we'll use the method defined at the very
bottom, os.userInfo([options]):
The os.userInfo([options]) method gets called and returns various information
about the currently logged-in user, such as the username, and this is what we'll
pull off:
Using the username that comes from the OS, we can customize the
greeting.txt
file so that instead of Hello world! it can say Hello Gary!.
To get started, we have to require OS. This means that we'll go back inside
Atom. Now, just below where I created my
fs
constant, I'll create a new constant
called
os
, setting it equal to
require()
; this gets called as a function and passes one
argument, the module name,
os
, as shown here:
console.log('Starting app.');
const fs = require('fs'); const
os = require('os');
fs.appendFile('greetings.txt', 'Hello world!');
From here, we can start calling methods available on the OS module, such as
os.userInfo([optional]).
Let's make a new variable called
user
to store the result. The variable user will
get set equal to
os.userInfo
, and we can call
userInfo
without any arguments:
console.log('Starting app.');
const fs = require('fs'); const
os = require('os');
var user = os.userInfo(); fs.appendFile('greetings.txt',
'Hello world!');
Now, before we do anything with the
fs.appendFile
line, I'll comment it out and
print the contents of the user variable using
console.log
:
console.log('Starting app.');
const fs = require('fs'); const
os = require('os');
var user = os.userInfo(); console.log(user);
// fs.appendFile('greetings.txt', 'Hello world!');
This will let us explore exactly what we get back. Over in Terminal, we can
rerun our program using the up arrow key and enter key, and right here in the
following code, you see that we have an object with a few properties:
We have
uid
,
gid
,
username
,
homedir
, and
shell
. Depending on your OS, you'll not have
all of these, but you should always have the
username
property. This is the one we
care about.
This means that back inside Atom, we can use
user.username
inside of
appendFile
. I'll
remove the
console.log
statement and uncomment our call to
fs.appendFile
:
console.log('Starting app.');
const fs = require('fs'); const
os = require('os');
var user = os.userInfo(); fs.appendFile('greetings.txt',
'Hello world!');
Now, where we have
world
in the
fs.appendFile
, we'll swap it with
user.username
. There
are two ways we can do this.
Concatenating user.username
The first way is to remove
world!
and concatenate
user.username
. Then we can
concatenate another string using the
+
(plus) operator, as shown in the following
code:
console.log('Starting app.');
const fs = require('fs');
const os = require('os');
var user = os.userInfo();
fs.appendFile('greetings.txt', 'Hello' + user.username + '!');
Now if we run this, everything is going to work as expected. Over in Terminal,
we can rerun our app. It prints
Starting app
:
Over in the
greetings.txt
file, you should see something like
Hello Gary!
printing to the
screen, as shown here:
Using the
fs
module and the
os
module, we were able to grab the user's
username, create a new file, and store it.
Using template strings
The second way to swap
world
with
user.username
in the
fs.appendFile
is, using an ES6
feature known as template strings. Template strings start and end with the
`
(tick) operator, which is available to the left of the 1 key on your keyboard. Then
you type things as you normally would.
This means that we'll first type
hello
, then we'll add a space with the
!
(exclamation) mark, and just before
!
, we will put the name:
console.log('Starting app.');
const fs = require('fs'); const
os = require('os');
var user = os.userInfo();
fs.appendFile('greetings.txt', `Hello !`);
To insert a JavaScript variable inside your template string, you use the
$
(dollar)
sign followed by opening and closing curly braces. Then we will just reference a
variable such as
user.username
:
console.log('Starting app.');
const fs = require('fs');
const os = require('os');
var user = os.userInfo();
fs.appendFile('greetings.txt', `Hello ${user.username}!`);
Notice that the Atom editor actually picks up on the syntax of curly
braces.
This is all it takes to use template strings; it's an ES6 feature available because
you're using Node v6. This syntax is much easier to understand and update than
the string/concatenation version we saw earlier.
If you run the code, it will produce the exact same output. We can run it, view
the text file, and this time around, we have
Hello Gary!
twice, which is what we
want here:
With this in place, we are now done with our very basic example and we're ready
to start creating our own files for our notes application and requiring them inside
app.js
in the next section.
First up, you learned that we can use
require
to load in modules. This lets us take
existing functionality written by either the Node developers, a third-party library,
or ourselves, and load it into a file so that it can be reusable. Creating reusable
code is essential for building large apps. If you have to build everything in an
app every time, no one would ever get anything done because they would get
stuck at building the basics, things such as HTTP servers and web servers. There
are already modules for such stuff, and we'll be taking advantage of the great
npm community. In this case, we used two built-in modules,
fs
and
os
. We loaded
them in using require and we stored the module results inside two variables.
These variables store everything available to us from the module; in the case of
fs
, we use the
appendFile
method, and in the case of OS, we use the
userInfo
method. Together, we were able to grab the username and save it into a file,
which is fantastic.
Require own files
In this section, you will learn how to use
require()
to load in other files that you
created inside your project. This will let you move functions outside
app.js
into
more specific files; this will make your application easier to scale, test, and
update. To get started, the first thing we'll do is to make a new file.
Making a new file to load other
files
In the context of our notes app, the new file will store various functions for
writing and reading notes. As of now, you don't need to worry about that
functionality, as we'll get into the detail later in the section, but we will create the
file where it will eventually live. This file will be
notes.js
, and we'll save it inside
the root of our application, right alongside
app.js
and
greetings.txt
, as shown here:
For the moment, all we'll do inside
notes
is to use
console.log
to print a little log
showing the file has been executed using the following code:
console.log('Starting notes.js');
Now, we have
console.log
on the top of
notes
and one on the top of
app.js
. I'll
change
console.log
in the
app.js
from
Starting app.
to
Starting app.js
. With this in place,
we can now require the notes file. It doesn't export any functionality, but that's
fine.
By the way, when I say export, I mean the notes file doesn't have
any functions or properties that another file can take advantage of.
We'll look at how to export stuff later in the section. For now though, we'll load
our module in much the same way we loaded in the built-in Node modules.
Let's make
const
; I'll call this one notes and set it equal to the return result from
require():
console.log('Starting app.js');
const fs = require('fs'); const
os = require('os'); const notes
= require('');
var user = os.userInfo();
fs.appendFile('greetings.txt', `Hello ${user.username}!`);
Inside the parentheses, we will pass in one argument that will be a string, but it
will be a little different. In the previous section, we typed in the module name,
but what we have in this case is not a module, but a file,
notes.js
. What we need
to do is to tell Node where that file lives using a relative path.
Now, relative paths start with
./
(a dot forward slash), which points to the current
directory that the file is in. In this case, this points us to the
app.js
directory,
which is the root of our project
notes-node
. From here, we don't have to go into
any other folders to access
notes.js
, it's in the root of our project, so we can type
its name, as shown in the following code:
console.log('Starting app.js');
const fs = require('fs');
const os = require('os');
const notes = require('./notes.js'); var user
= os.userInfo();
fs.appendFile('greetings.txt', `Hello ${user.username}!`);
With this in place, we can now save
app.js
and see what happens when we run
our application. I'll run the app using the
node app.js
command:
As shown in the preceding code output, we get our two logs. First, we get
Starting app.js
and then we get
Starting notes.js
. Now,
Starting notes.js
comes from the
note.js
file, and it only runs because we required the file inside of
app.js
.
Comment out this command line from the
app.js
file, as shown here:
console.log('Starting app.js');
const fs = require('fs');
const os = require('os');
// const notes = require('./notes.js'); var
user = os.userInfo();
fs.appendFile('greetings.txt', `Hello ${user.username}!`);
Save the file, and rerun it from Terminal; you can see the
notes.js
file never
executes because we never explicitly touch it.
We never call it inside Terminal as we do in the preceding example, and we
never require.
For now though, we will be requiring it, so I'll uncomment that line.
By the way, I'm using command / (forward slash) to comment and
uncomment lines quickly. This is a keyboard shortcut available in
most text editors; if you're on Windows or Linux, it might not be
command, it might be Ctrl or something else.
Exporting files from note.js to use
in app.js
For now though, the focus will be to export something from
notes.js
which we
can use in
app.js
. Inside
notes.js
(actually, inside all of our Node files), we have
access to a variable called
module
. I'll use
console.log
to print
module
to the screen so
that we can explore it over in Terminal, as shown here:
console.log('Starting notes.js');
console.log(module);
Let's rerun the file to explore it. As shown in the following screenshot, we get a
pretty big object, that is, different properties related to the
notes.js
file:
Now, to tell the truth, we'll not be using most of these properties. We have things
such as
id
,
exports
,
parent
, and
filename
. The only one property we'll ever use in this
book is
exports
.
The
exports
object on the
module
property and everything on this object gets
exported. This object gets set as the
const
variable,
notes
. This means that we can
set properties on it, they will get set on notes, and we can use them inside
app.js
.
A simple example of the working
of the exports object
Let's take a quick look at how that works. What we'll do is to define an
age
property using
module.exports
, the object we just explored over in Terminal. Also,
we know that it's an object because we can see it in the preceding screenshot
(
exports: {}
); this means that I can add a property,
age
, and set it equal to my age,
which is
25
, as shown here:
console.log('Starting notes.js');
module.exports.age = 25;
Then I can save this file and move into
app.js
to take advantage of this new
age
property. The
const
variable notes will be storing all of my exports, in the present
case, just age.
In
fs.appendFile
, after the
greeting.txt
file, I'll add
You are
followed by the age. Inside
template strings, we will use
$
with curly braces,
notes.age
, and a period at the end,
as shown here:
console.log('Starting app.js');
const fs = require('fs');
const os = require('os');
const notes = require('./notes.js'); var user
= os.userInfo();
fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
Now our greeting should say
Hello Gary! You are 25
. It's getting the
25
value from our
separate file (that is,
note.js
), which is fantastic.
Let's take a quick moment to rerun the program over in Terminal using the up
arrow key and enter keys:
Back inside the app, we can open
greetings.txt
, and as shown in the following
screenshot, we have
Hello Gary! You are 25
:
Using
require()
, we were able to require a file that we created, and this file stored
some properties that were advantageous to the rest of the project.
Exporting the functions
Now, obviously, the preceding example is pretty contrived. We'll not be
exporting static numbers; the real goal of exports is to be able to export functions
that get used inside
app.js
. Let's take a quick moment to export two functions. In
the
notes.js
file, I'll set
module.exports.addnote
equal to a function; the
function
keyword
followed by opening and closing parentheses, which is followed by the curly
braces:
console.log('Starting notes.js');
module.exports.addNote = function () {
}
Now, throughout the course, I'll be using arrow functions where I can, as shown
in the preceding code. To convert a regular ES5 function into an arrow function,
all you do is remove the
function
keyword and replace it with an
=>
sign right
between the parentheses and the opening curly braces, as shown here:
console.log('Starting notes.js');
module.exports.addNote = () => {
}
Now, there are some more subtleties to arrow functions that we'll
be talking about throughout the book, but if you have an
anonymous function, you can swap it with an arrow function
without any problems. The big difference is that the arrow function
is not going to bind the
() => {}
keyword or the arguments array,
which we'll be exploring throughout the book. So if you do get some
errors, it's good to know that the arrow function could be the cause.
For now though, we'll keep things really simple, using
console.log
to print
addNote
.
This will let us know that the
addNote
function was called. We'll return a string,
'New note'
, as shown here:
console.log('Starting notes.js');
module.exports.addNote = () => {
console.log('addNote');
return 'New note';
};
Now, the
addNote
function is being defined in
notes.js
, but we can take advantage of
it over in
app.js
.
Let's take a quick second to comment out both the
appendFile
and user line in
app.js:
console.log('Starting app.js');
const fs = require('fs'); const
os = require('os');
const notes = require('./notes.js');
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
I'll add a variable, call the result, (
res
for short), and set it equal to the return
result from
notes.addNote
:
console.log('Starting app.js');
const fs = require('fs'); const
os = require('os');
const notes = require('./notes.js'); var res
= notes.addNote();
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
Now, the
addNote
function is a dummy function for the moment. It doesn't take
any arguments and it doesn't actually do anything, so we can call it without any
arguments.
Then we'll print the result variable, as shown in the following code, and we
would expect the result variable to be equal to the
New note
string:
console.log('Starting app.js');
const fs = require('fs'); const
os = require('os');
const notes = require('./notes.js');
var res = notes.addNote(); console.log(res);
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
If I save both of my files (
app.js
and
notes.js
) and rerun things from Terminal, you
can see that
New note
prints to the screen at the very end and just before
addNote
prints:
This means that we successfully required the notes file we called
addNote
, and its
return result was successfully returned to
app.js
.
Using this exact pattern, we'll be able to define our functions for adding and
removing notes over in our
notes.js
file, but we'll be able to call them anywhere
inside of our app, including in
app.js
.
Exercise – adding a new function
to the export object
Now it's time for a quick challenge. What I'd like you to do is make a new
function in
notes.js
called
add
. This
add
function will get set on the
exports
object.
Remember,
exports
is an object, so you can set multiple properties.
This
add
function will take two arguments,
a
and
b
; it'll add them together and
return the result. Then over in
app.js
, I'd like you to call that
add
function, passing
in two numbers, whatever you like, such as
9
and
-2
, then print the result to the
screen and make sure it works correctly.
You can get started by removing the call to
addNote
since this will not
be needed for the challenge.
So, take a moment, create that
add
function inside
notes.js
, call it inside
app.js
, and
make sure the proper result prints to the screen. How'd it go? Hopefully, you
were able to make that function and call it from
app.js
.
Solution to the exercise
The first step in the process will be to define the new function. In
notes.js
, I'll set
module.exports.add
equal to that function, as shown here:
console.log('Starting notes.js');
module.exports.addNote = () => {
console.log('addNote');
return 'New note';
};
module.exports.add =
Let's set it equal to an arrow function. If you used a regular function, that is
perfectly fine, I just prefer using the arrow function when I can. Also, inside
parentheses, we will be getting two arguments, we'll be getting
a
and
b
, as shown
here:
console.log('Starting notes.js');
module.exports.addNote = () => {
console.log('addNote');
return 'New note';
};
module.exports.add = (a, b) => {
};
All we need to do is return the result, which is really simple. So we'll enter
return
a + b
:
console.log('Starting notes.js');
module.exports.addNote = () => {
console.log('addNote');
return 'New note';
};
module.exports.add = (a, b) => { return
a + b;
};
Now, this was the first part of your challenge, defining a utility function in
notes.js
; the second part was to actually use it over in
app.js
.
In
app.js
, we can use our function by printing the
console.log
result with a colon
:
(this is just for formatting). As the second argument, we'll print the actual results,
notes.add
. Then, we'll add up two numbers; we'll add
9
and
-2
, as shown in this
code:
console.log('Starting app.js');
const fs = require('fs'); const
os = require('os');
const notes = require('./notes.js');
console.log('Result:', notes.add(9, -2));
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
The result in this case should be
7
. If we run the program you can see that we get
just that,
7
prints to the screen:
If you were able to get this, congratulations, you successfully completed one of
your first challenges. These challenges will be sprinkled throughout the book
and they'll get progressively more complex. But don't worry, we'll keep the
challenges pretty explicit; I'll tell you exactly what I want and exactly how I
want it done. Now, you can play around with different ways to do it, the real goal
is to just get you writing code independent of following someone else's lead.
That is where the real learning happens.
In the next section, we will explore how to use third-party modules. From there,
we'll start building the notes application.
Third-party modules
You now know two out of the three ways to use
require()
, and in this section,
we'll explore the last way, which is to require a package you've installed from
npm. As I mentioned in the first chapter, npm is a big part of what makes Node
so fantastic. There is a huge community of developers that have created
thousands of packages that already solve some of the most common problems in
Node applications. We will be taking advantage of quite a few packages
throughout the book.
Creating projects using npm
modules
Now, in the npm packages, there's nothing magical, it's regular Node code that
aims to solve a specific problem. The reason you'd want to use it is so you don't
have to spend all your time writing these utility functions that already exist; not
only do they exist, they've been tested, they've been proven to work, and others
have used them and documented them.
Now, with all that said, how do we get started? Well, to get started, we actually
have to run a command from the Terminal to tell our application we want to use
npm modules. This command will be run over in the Terminal. Make sure you've
navigated inside your project folder and inside the
notes-node
directory. Now,
when you installed Node, you also installed something called npm.
At one point, npm stood for
Node package manager
, but that's now
a running joke because there are plenty of things on npm that are
not specific to Node. A lot of frontend frameworks, such as jQuery
and react, now live on npm as well, so they've pretty much ditched
the Node package manager explanation and now on their site, they
cycle through a bunch of hilarious things that happen to match up
with npm.
We will be running some npm commands and you can test that you have it
installed by running
npm
, a space, and
-v
(we're running npm with the
v
flag). This
should print the version, as shown in the following code:
It's okay if your version is slightly different, that's not important; what is
important is that you have npm installed.
Now, we'll run a command called
npm init
in Terminal. This command will
prompt us to fill out a few questions about our npm project. We can run the
command and we can cycle through the questions, as shown in the following
screenshot:
In the preceding screenshot, at the top is a quick description of what's happening,
and down below it'll start asking you a few questions, as shown in the following
screenshot:
The questions include the following:
name: Your name can't have uppercase characters or spaces; you can use
notes-node
, for example. You can hit enter to use the default value, which is
in parentheses.
version: 1.0.0 works fine too; we will leave most of these at their default
value.
description: We can leave this empty at the moment.
entry point: This will be
app.js
, make sure that shows up properly.
test command: We'll explore testing later in the book, so for now, we can
leave this empty.
git repository: We'll leave that empty for now as well.
keywords: These are used for searching for modules. We'll not be
publishing this module so we can leave those empty.
author: You might as well type your name.
license: For the license, we'll stick with ISC at the moment; since we're not
publishing it, it doesn't really matter.
After answering these questions, if we hit enter, we'll get the following on our
screen and a final question:
Now, I want to dispel the myth that this command is doing anything magical. All
this command is doing is creating a single file inside your project. It'll be in the
root of the project and it's called
package.json
, and the file will look exactly like
the preceding screenshot.
To the final question, as shown down below in the preceding image, you can hit
enter or type
yes
to confirm that this is what you want to do:
Now that we have created the file, we can actually view it inside our project. As
shown in the following code, we have the
package.json
file:
{
"name": "notes-node",
"version": "1.0.0",
"description": "",
"main": "app.js", "scripts":
{
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
And this is all it is, it's a simple description of your application. Now, as I
mentioned, we'll not be publishing our app to npm, so a lot of this information
really isn't important to us. What is important, though, is that
package.json
is
where we define the third-party modules we want to install in our application.
Installing the lodash module in
our app
To install a module in the app, we will run a command over in the Terminal. In
this chapter, we'll be installing a module called
lodash
. The
lodash
module comes
with a ton of utility methods and functions that make developing inside Node or
JavaScript a heck of a lot easier. To take a look at what exactly we're getting
into, let's move into the browser.
We'll to go to
https://www.npmjs.com
. Then we'll search for the package,
lodash
, and
you can see it comes up, as shown in the following screenshot:
When you click on it, you should be taken to the package page, and the package
page will show you a lot of statistics about the module and the documentation, as
shown here:
Now, I use the
lodash
package page when I'm looking for new modules; I like to
see how many downloads it has and when it was last updated. On the package
page, you can see it was updated recently, which is great it means the package is
most likely compatible with the latest versions of Node, and if you go further
down the page, you can see this is actually one of the most popular npm
packages, with over a million downloads a day. We will be using this module to
explore how to install npm modules and how to actually use them in a project.
Installation of lodash
To install
lodash
, the first thing you need to grab is just a module name, which is
lodash
. Once you have that information, you're ready to install it.
Coming to Terminal, we'll run the
npm install
command. After installing, we'll
specify the module,
lodash
. Now, this command alone would work; what we'll
also do, though, is provide the
save
flag.
The
npm install lodash
command will install the module, and the
save
flag,
--
(two)
hyphens followed by the word
save
, will update the contents of the
package.json
file. Let's run this command:
npm install loadsh --save
The preceding command will go off to the npm servers and fetch the code and
install it inside your project, and any time you install an npm module, it'll live in
your project in a
node_modules
folder.
Now, if you open that
node_modules
folder, you'll see the
lodash
folder as shown in
the following code. This is the module that we just installed:
{
"name": "notes-node",
"version": "1.0.0",
"description": "",
"main": "app.js", "scripts":
{
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.4"
}
}
As you can see over in
package.json
in the preceding figure, we've also had some
updates automatically take place. There's a new
dependencies
attribute that has an
object with key value pairs, where the key is the module we want to use in our
project and the value is the version number, in this case, the most recent version,
version
4.17.4
. With this in place, we can now require our module inside the
project.
Over inside
app.js
, we can take advantage of everything that comes in
lodash
by
going through the same process of requiring it. We'll make a
const
, we'll name
that
const _
, (which is a common name for the
lodash
utility library), and we'll set it
equal to
require()
. Inside the require parentheses, we'll pass in the module name
exactly as it appears in the
package.json
file. This is the same module name you
used when you ran
npm install
. Then, we'll type
lodash
, as shown here:
console.log('Starting app.js');
const fs = require('fs'); const os
= require('os'); const _ =
require('lodash');
const notes = require('./notes.js');
console.log('Result:', notes.add(9, -2));
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
Now, the order of operations is pretty important here. Node will first look for a
core module with the name
lodash
. It'll not find one because there is no core
module, so the next place it will look is the
node_modules
folder. As shown in the
following code, it will find
lodash
and load that module, returning any of the
exports it provides:
console.log('Starting app.js');
const fs = require('fs'); const os
= require('os'); const _ =
require('lodash');
const notes = require('./notes.js');
console.log('Result:', notes.add(9, -2));
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
Using the utilities of lodash
With the exports in place, we can now take advantage of some of the utilities that
come with Lodash. We'll quickly explore two in this section, and we'll be
exploring more throughout the book since Lodash is basically just a set of really
handy utilities. Before we do, we should take a look at the documentation so we
know exactly what we're getting into.
This is a really common step when you're using an npm module:
first, you install it; second, you've got to look at those docs and
make sure that you can get done what you want to get done.
On the npm page, click the lodash link given there, or go to
lodash.com
and click
the API Documentation page, as shown here:
You can view all of the various methods you have available to you, as shown in
the following screenshot:
In our case, we'll be using command + F (Ctrl + F for Windows users) to search
for
_.isString
. Then in the docs, we can click on it, opening it up in the main
page, as shown in the following screenshot:
The
_.isString
is a utility that comes with
lodash
, and it returns
true
if the variable
you pass in is a string, and it returns
false
if the value you pass in is not a string.
And we can prove that by using it over in Atom. Let's use this.
Using the _.isString utility
To use the
_.isString
utility, we'll add
console.log
in
app.js
to show the result to the
screen and we'll use
_.isString
, passing in a couple of values. Let's pass in
true
first, then we can duplicate this line and we'll pass in a string such as
Gary
, as
shown here:
console.log('Starting app.js');
const fs = require('fs'); const os
= require('os'); const _ =
require('lodash');
const notes = require('./notes.js');
console.log(_.isString(true));
console.log(_.isString('Gary'));
// console.log('Result:', notes.add(9, -2));
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
We can run our project over in the Terminal using the same command we've
used previously,
node app.js
, to run our file:
When we run the file, we get our two prompts that we've started both files, and
we get
false
and then
true
.
false
comes because the Boolean is not a string, and
true
comes up because
Gary
is indeed a string, so it passes the test of
_.isString
. This
is one of the many utility functions that comes bundled with
lodash
.
Now,
lodash
can do a lot more than simple type checking. It comes with a bunch
of other utility methods we can take advantage of. Let's explore one more utility.
Using _.uniq
Back inside the browser, we can use command + F again to search for a new
utility, which is
_.uniq
:
This unique method, simply takes an array and it returns that array with all
duplicates removed. That means if I have the same number a few times or the
same string, it'll remove any duplicates. Let's run this.
Back inside Atom, we can add this utility into our project, we'll comment out our
_.isString
calls and we will make a variable called
filteredArray
. This will be the
array without the duplicates, and what we'll do is call, after the equal sign,
_.uniq
.
Now, as we know, this takes an array. And since we're trying to use the unique
function, we'll pass in an array with some duplicates. Use your name twice as a
string; I'll use my name once, followed by the number
1
, followed by my name
again. Then I can use
1
,
2
,
3
, and
4
as shown here:
console.log('Starting app.js');
const fs = require('fs'); const os
= require('os'); const _ =
require('lodash');
const notes = require('./notes.js');
// console.log(_.isString(true));
// console.log(_.isString('Gary'));
var filteredArray = _.uniq(['Gary', 1, 'Gary', 1, 2, 3, 4]); console.log();
// console.log('Result:', notes.add(9, -2));
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
Now, if things go as planned, we should get an array with all the duplicates
removed, which means we'll have one instance of
Gary
, one instance of
1
, and
then
2
,
3
, and
4
, which don't have duplicates.
The last thing to do is to print that using
console.log
so we can view it inside the
Terminal. I'll pass in this
filteredArray
variable to our
console.log
statement as
shown in the following code:
console.log('Starting app.js');
const fs = require('fs'); const os
= require('os'); const _ =
require('lodash');
const notes = require('./notes.js');
// console.log(_.isString(true));
// console.log(_.isString('Gary'));
var filteredArray = _.uniq(['Gary', 1, 'Gary', 1, 2, 3, 4]); console.log(filteredArray);
// console.log('Result:', notes.add(9, -2));
// var user = os.userInfo();
//
// fs.appendFile('greetings.txt', `Hello ${user.username}! You are ${notes.age}.`);
From here, we can run our project inside Node. I'll use the last command, then I
can press the enter key, and you can see we get our array with all duplicates
removed, as shown in the following code output:
We have one instance of the string
Gary
, one instance of the number
1
, and then
we have
2
,
3
,
4
, exactly what we expected.
The
lodash
utility really is endless. There are so many functions that it can be
kind of overwhelming to explore at first, but as you start creating more
JavaScript and Node projects, you'll find yourself solving a lot of the same
problems over and over again when it comes to sorting, filtering, or type
checking, and in that case, it's best to use a utility such as
lodash
to get that lifting
done. The
lodash
utility is great for the following reasons:
You don't have to keep rewriting your methods
It is well tested and it has been tried in production
If there were any issues, they've been sorted out by now.
The node_modules folder
Now that you know how to use a third-party module, there is one more thing I
want to discuss. That is the
node_modules
folder in general. When you take your
Node project and you put it on GitHub, or you're copying it around or sending it
to a friend, the
node_modules
folder really shouldn't be taken with you.
The
node_modules
folder contains generated code. This is not code you've written
and you should never make any updates to the files inside Node modules
because there's a pretty good chance they'll get overwritten next time you install
some modules.
In our case, we've already defined the modules and the versions inside
package.json
as shown in the following code because we used that handy
save
flag:
{
"name": "notes-node",
"version": "1.0.0",
"description": "",
"main": "app.js", "scripts":
{
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.4"
}
}
This actually means we can delete the
node_modules
folder completely. Now, we
can copy the folder and give it to a friend, we can put it on GitHub, or whatever
we want to do. When we want to get that
node_modules
folder back, all we have to
do inside the Terminal is run the
npm install
command without any module names
or any flags.
This command, when run without any names or flags, is going to load in your
package.json
file, grab all of the dependencies and install them. After running this
command, the
node_modules
folder is going to look exactly as it looked before we
deleted it. Now, when you are using Git and GitHub, instead of deleting the
node_modules
folder, you'll just ignore it from your repository.
Now, what we have explored so far is a process we'll be going through a lot
more throughout the book. So if npm still seems foreign or you're not quite sure
why it's even useful, it will become clear as we do more with our third-party
modules, rather than just type checking or looking for unique items in an array.
There's a ton of power behind the npm community and we'll be harnessing that
to our fullest as we make real-world apps.
Global modules
One of the major complaints I get is the fact that students have to restart the app
from the Terminal every time they want to see the changes they just made inside
their text editor. So, in this section, we'll take a look at how we can automatically
restart our app as we make changes to the file. That means if I change from
Gary
to
Mike
and save it, it will automatically restart over in the Terminal.
Installing the nodemon module
Now, to automatically restart our app as we make changes to a file, we have to
install a command-line utility, and we'll do this using npm. To get started, we'll
go to Google Chrome (or the browser you are using) and head over to
https://www.
npmjs.com
, as we did previously in the Installing the lodash module in our app
section, and the module we're looking for is called
nodemon
.
The nodemon will be responsible for watching our app for changes and
restarting the app when those changes occur. Right here, as we see in the
following screenshot, we can view the docs for nodemon as well as various other
things such as current version numbers and so on:
You will also notice that it's a really popular module, with over 30,000
downloads a day. Now, this module is a little different from the one we used in
the last section, that is,
lodash
. The
lodash
got installed and added into our project's
package.json
file as shown in the following code block:
{
"name": "notes-node",
"version": "1.0.0",
"description": "",
"main": "app.js", "scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": { "lodash":
"^4.17.4"
}
}
That means it went into our
node_modules
folder and we were able to require it in
our
app.js
file (refer to the previous section for more detail). Nodemon, however,
works a little differently. It's a command-line utility that gets executed from the
Terminal. It will be a completely new way of starting our application, and to
install modules to be run from the command line, we have to tweak the
install
command that we used in the last section.
For now, we can start off much the same way, though. We'll use
npm install
and
type the name just like we did in the Installing the lodash module in our app
section, but instead of using the
save
flag, we'll use the
g
flag, which is short for
global, as shown here:
npm install nodemon -g
This command installs
nodemon
as a global utility on your machine, which means
it'll not get added to your specific project and you'll never require
nodemon
.
Instead, you'll be running the
nodemon
command from Terminal, as shown here:
When we install
nodemon
using the preceding command, it'll go off to npm and
fetch all of the code that comes with
nodemon
.
And it'll add it into the installation where Node and npm live on your machine,
outside the project you're working on.
The
npm install nodemon -g
command could be executed from anywhere in your
machine; it does not need to be executed from the project folder since it doesn't
actually update the project at all. With this in place, though, we now have a
brand new command on our machine,
nodemon
.
Executing nodemon
Nodemon will get executed as Node did, where we type the command and then
we type the file we want to start. In our case,
app.js
is the root of our project.
When you run it, you'll see a few things, as shown here:
We'll see a combination of our app's output, along with
nodemon
logs that show
you what's happening. As shown in the preceding code, you can see the version
nodemon
is using, the files it's watching, and the command it actually ran. Now, at
this point, it's waiting for more changes; it already ran through the entire app and
it'll keep running until another change happens or until you shut it down.
Inside Atom, we'll make a few changes to our app. Let's get started by changing
Gary to Mike in app.js, and then we'll change the filteredArray variable to var filteredArray =
_.uniq(['Mike']), as shown in the following code:
console.log('Starting app.js');
const fs = require('fs'); const os
= require('os'); const _ =
require('lodash');
const notes = require('./notes.js');
// console.log(_.isString(true));
// console.log(_.isString('Gary')); var
filteredArray = _.uniq(['Mike']);
console.log(filteredArray);
Now, I'll be saving the file. In the Terminal window, you can see the app
automatically restarted, and within a split second, the new output is shown on
the screen:
As shown in the preceding screenshot, we now have our array with one item of
string,
Mike
. And this is the real power of
nodemon
.
You can create your applications and they will automatically restart over in the
Terminal, which is super useful. It'll save you a ton of time and a ton of
headaches. You won't have to switch back and forth every time you make a small
tweak. This also prevents a ton of errors where you are running a web server,
you make a change, and you forget to restart the web server. You might think
your change didn't work as expected because the app is not working as expected,
but in reality, you just never restarted the app.
For the most part, we will be using
nodemon
throughout the book since it's super
useful. It's only used for development purposes, which is exactly what we're
doing on our local machine. Now, we'll move forward and start exploring how
we can get input from the user to create our notes application. That will the topic
of the next few sections.
Before we get started, we should clean up a lot of the code we've already written
in this section. I'll remove all of the commented-out code in
app.js
. Then, I'll
simply remove
os
, where we have
fs
,
os
and
lodash
, since we'll not be using it
throughout the project. I'll also be adding a space between the third-party and
Node modules and the files I've written, which are as follows:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
I find this to be a good syntax that makes it a lot easier to quickly scan for either
third-party or Node modules, or the modules that I've created and required.
Next up, over in
notes.js
, we'll remove the
add
function; this was only added for
demonstration purposes, as shown in the following figure. Then we can save
both the
notes.js
and
app.js
files, and
nodemon
will automatically restart:
console.log('Starting notes.js');
module.exports.addNote = () => {
console.log('addNote');
return 'New note';
};
module.exports.add = (a, b) => { return
a + b;
};
Now we can remove the
greetings.txt
file. That was used to demonstrate how the
fs
module works, and since we already know how it works, we can wipe that file.
And last but not least, we can always shut down
nodemon
using Ctrl + C. Now
we're back at the regular Terminal.
And with this in place, now we should move on, figuring out how we can get
input from the user, because that's how users can create notes, remove notes, and
fetch their notes.
Getting input
If a user wants to add a note, we need to know the note's title as well as the body
of the note. If they want to fetch a note, we need to know the title of the note
they want to fetch, and all this information needs to come into our app. And note
apps, don't really do anything cool until they get this dynamic user input. This is
what makes your scripts useful and awesome.
Now, throughout the book, we'll be creating note apps that get input from the
user in a lot of different ways. We'll be using socket I/O to get real-time info
from a web app, we'll be creating our own API so other websites and servers can
make Ajax requests to our app, but in this section, we'll start things off with a
very basic example of how to get user input.
We'll be getting input from the user inside the command line. That means when
you run the app in the command line, you'll be able to pass in some arguments.
These arguments will be available inside Node, and then we can do other things
with them, such as create a note, delete a note, or return a note.
Getting input from the user inside
the command line
To start things off, let's run our app from the Terminal. We'll run it pretty
similarly to how we ran it in the earlier sections: we'll start with
node
(I'm not
using
nodemon
since we'll be changing the input), then we'll use
app.js
, which is the
file we want to run, but then we can still type other variables.
We can pass all sorts of command-line arguments in. We could
have a command, and this would tell the app what to do, whether
you want to add a note, remove a note, or list a note.
If we want to add a note, that might look as a command shown in the following
code:
node app.js add
This command will add a note; we can remove a note using the
remove
command,
as shown here:
node app.js remove
And we could list all of our notes using the
list
command:
node app.js list
Now, when we run this command, the app is still going to work as expected. Just
because we passed in a new argument doesn't mean our app is going to crash:
And we actually have access to the
list
argument already, we're just not using it
inside the application.
To access the command-line arguments your app was initialized with, you'll
want to use that
process
object that we explored in the first chapter.
We can log out all of the arguments using
console.log
to print them to the screen;
it's on the process object, and the property we're looking for is
argv
.
The
argv
object is short for arguments vector, or in the case of
JavaScript, it's more like an arguments array. This will be an array
of all the command-line arguments passed in, and we can use them
to start creating our application.
Now save
app.js
and it'll look like the following:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
console.log(process.argv);
Then we'll rerun this file:
Now, as shown in the preceding command output, we have three items which are
as follows:
The first one points to the executable for Node that was used.
The second one points to the app file that was started; in this case, it was
app.js.
The third one is where our command-line arguments start to come into play.
In it, we have our
list
showing up as a string.
That means we can access that third item in the array, and that will be the
command for our notes application.
Accessing the command-line
argument for the notes
application
Let's access the command-line argument in the array now. We'll make a variable
called
command
, and set it equal to
process.argv
, and we'll grab the item in the third
position (which is
list
, as shown in the preceding command output), which is the
index of two as shown here:
var command = process.argv[2];
Then we can log that out to the screen by logging out
command
the string. Then, as
the second argument, I'll pass in the actual command that was used:
console.log('Command: ' , command);
And this is just a simple log to keep track of how the app is getting executed.
The cool stuff is going to come when we add if statements that do different
things depending on that command.
Adding if/else statements
Let's create an if/else block below the console.log('Command: ', command);. We'll add
if (command === 'add'), as shown here:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add')
In this case, we'll go through the process of adding a
new note
. Now, we're not
specifying the other arguments here, such as the title or the body (we'll discuss
that in later sections). For now,
if
the
command
does equal
add
, we'll use
console.log
to
print
Adding new note
, as shown in the following code:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') { console.log('Adding
new note');
}
And we can do the exact same thing with a command such as
list
. We'll add
else if
(command === 'list')
, as shown here:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list')
If the command does equal the string
list
, we'll run the following block of code
using
console.log
to print
Listing all notes
. We can also add an
else
clause if there is no
command, which is
console.log ('Command not recognized')
, as shown here:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else {
console.log('Command not recognized');
}
With this in place, we can now rerun our app for a third time, and this time
around, you'll see we have the command equal to list, and listing all notes shows
up, as shown in the following code:
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else {
console.log('Command not recognized');
}
This means we were able to use our argument to run different code. Notice that
we didn't run
Adding new note
and we didn't run
Command not recognized
. We could,
however, switch the
node app.js
command from
list
to
add
, and in that case, we'll
get
Adding new note
printing, as shown in the following screenshot:
And if we run a command that doesn't exist, for example
read
, you can see
Command not
recognized
prints as shown in the following screenshot:
Exercise – adding two else if
clauses to an if block
Now, what I'd like you to do is add two more
else if
clauses to our
if
block,
which will be as follows:
One will be for the
read
command, which will be responsible for getting an
individual note back
Another one called
remove
will be responsible for removing the note
All you have to do is add the
else if
statement for both of them, and then just put
a quick
console.log
printing something like
Fetching note
or
Removing note
.
Take a moment to knock that out as your challenge for this section. Once you
add those two
else if
clauses, run both of them from the Terminal and make sure
your log shows up. If it does show up, you are done, you can move ahead with
this section.
Solution to the exercise
For the solution, the first thing I'll do is to add an
else if
for
read
. I'll open and
close my curly braces and hit enter right in the middle so everything gets
formatted correctly.
In the
else if
statement, I'll check whether the
command
variable equals the string
read
, as shown here:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else if () {
} else {
console.log('Command not recognized');
}
In the future, we'll be calling methods that update our local
database with the notes.
For now, we'll use console.log to print Reading note: console.log('Starting
app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else if (command === 'read') {
} else {
console.log('Command not recognized');
}
The next thing you need to do is add an
else if
clause that checks whether the
command
equals
remove
. In the
else if
, I'll open and close my condition and hit enter
just as I did in the previous
else if
clause; this time, I'll add
if
the
command
equals
remove
, we want to remove the note. And in that case, all we'll do is to use
console.log
to print
Reading note
, as shown in the following code:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else if (command === 'read') {
console.log('Reading note');
} else {
console.log('Command not recognized');
}
And with this in place, we are done. If we refer to the code block, we've added
two new commands we can run over in the Terminal, and we can test those:
if (command === 'add') { console.log('Adding
new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else if (command === 'read') {
console.log('Reading note');
} else {
console.log('Command not recognized');
}
First up, I'll run node app.js with the read command, and Reading note shows up:
console.log('Starting app.js');
const fs = require('fs'); const _ =
require('lodash');
const notes = require('./notes.js');
var command = process.argv[2];
console.log('Command: ', command);
if (command === 'add') {
console.log('Adding new note');
} else if (command === 'list') {
console.log('Listing all notes');
} else if (command === 'read') {
console.log('Reading note');
} else if (command == 'remove') {
console.log('Removing note');
} else {
console.log('Command not recognized');
}
Then I'll rerun the command; this time, I'll be using
remove
. And when I do that,
Removing note
prints to the screen, as shown in this screenshot:
I'll wrap up my testing using a command that doesn't exist, and when I run that,
you can see
Command not recognized
shows up.
Getting the specific note
information
Now, what we did in the previous subsection is step 1. We now have support for
various commands. The next thing we need to figure out is how we'll get more
specific information. For example, which note do you want to remove? Which
note do you want to read? And what do you want the note text to be in the case
of adding a note? This is all information we need to get from the Terminal.
Now, getting it is going to be pretty similar to what we did earlier, and to show
you what it looks like, we'll print the entire
argv
object once again, using the
following command:
console.log(process.argv);
Over in the Terminal, we can now run a more complex command. Let's say we
want to remove a note using the
node app.js remove
command, and we'll do that by its
title. We might use the
title
argument, which looks like the following code:
node app.js remove --title
In this
title
argument, we have
--
(two) hyphens followed by the argument
name, which is
title
, followed by the
=
(equals) sign. Then we can type our note
title. Maybe the note title is
secrets
. This will pass the title argument into our
application.
Now, there are a couple of different ways you could format the
title
argument,
which are as follows:
You could have the title
secrets
like the one in the preceding command
You could have title equals secrets inside quotes, which will let us use
spaces in the title:
node app.js remove --title=secrets
You can remove the
=
(equals) sign altogether and simply put a space:
node app.js remove --title="secrets 2"
No matter how you choose to format your argument, these are all valid ways to
pass in the title.
As you see in the preceding screenshot, I am using double quotes
when wrapping my string. Now, if you switch to single quotes, it
will not break on Linux or OS X, but it will break on Windows. That
means when you're passing in command-line arguments such as the
title or the note body, you'll want to wrap your strings, when you
have spaces, in double quotes, not single. So, if you are using
Windows and you're getting some sort of unexpected behavior with
your arguments, make sure you're using double quotes instead of
single; that should fix the issue.
For the moment, I'll keep the
=
(equals) sign and the quotes and rerun the
command:
node app.js remove --title="secrets 2"
When I run the command, you can see in the following code output that we have
our two arguments:
These are the arguments that we don't need, then we have our
remove
command,
which is the third one, and we now have a new fourth string, the title that is
equal to
secrets 2
. And our argument was successfully passed into the
application. The problem is that it's not very easy to use. In the fourth string, we
have to parse out the key, which is
title
, and the value, which is
secrets 2
.
When we used the command, which was the third argument in the previous
section, it was a lot easier to use inside our app. We simply pulled it out of the
arguments array and we referenced it by using the command variable and
checking whether it equaled
add
,
list
,
read
, or
remove
.
Things get a lot more complex as we use different styles for passing in the
arguments. If we rerun the last command with a space instead of an
=
(equals)
sign, as shown in the following code, which is perfectly valid, our arguments
array now looks completely different:
In the preceding code output, you can see that we have the title as the fourth
item, and we have the value, which is
secrets 2
, as the fifth, which means we
have to add other conditions for parsing. And this turns into a pain really
quickly, which is why we will not do it.
We'll use a third-party module called yargs in the next chapter to make parsing
the command-line arguments effortless. Instead of having strings, as shown in
this one or the one we discussed earlier, we'll get an object where the title
property equals the
secrets 2
string. That will make it super easy to implement the
rest of the notes application.
Now, parsing certain types of command-line arguments, such as key value pairs,
becomes a lot more complex, which is why, in the next chapter, we'll be using
yargs to do just that.
Summary
In this chapter, we learned how to use require to load in modules that come with
Node.js. We created our files for our notes application and required them inside
app.js
. We explored how to use built-in modules and we explored how to use
modules we defined. We found out how to require other files that we created,
and how to export things such as properties and functions from those files.
We explored npm a little bit, how we can use
npm init
to generate a
package.json
file, and how we can install and use third-party modules. Next, we explored the
nodemon
module, using it to automatically restart our app as we make changes to a
file. Last, we learned how to get input from the user, which is needed to create
the notes application. We learned that we can use command-line arguments to
pass data into our app.
In the next chapter, we'll explore some more interesting Node fundamental
concepts, including yargs, JSON, and Refactor.